package com.rd.server.framework.config;

import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Map;
import java.util.StringJoiner;
import java.util.UUID;

/**
 * 操作日志切面
 * 用于记录系统接口调用日志
 *
 * @author 赵大永
 * @create: 2024-04-27 17:17
 */
@Aspect
@Slf4j
@Component
public class OperLogAspect {
    /**
     * 开始时间
     */
    private final ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 请求ID
     */
    private final ThreadLocal<String> requestId = new ThreadLocal<>();

    /**
     * 参数最大长度
     */
    private static final int MAX_PARAM_LENGTH = 200;

    /**
     * 响应结果最大长度
     */
    private static final int MAX_RESPONSE_LENGTH = 200;

    /**
     * 定义切点 - 拦截所有Controller层的请求
     */
    @Pointcut("execution(* com.rd.server.modules..*.controller.*.*(..))")
    public void operLogPointcut() {
    }

    /**
     * 环绕通知：在目标方法执行前后执行
     */
//    @Around("operLogPointcut()")
//    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
//        Object result = null;
//        try {
//            // 前置处理
//            doBefore(joinPoint);
//            // 执行目标方法
//            result = joinPoint.proceed();
//            // 后置处理
//            doAfterReturning(joinPoint, result);
//            return result;
//        } catch (Throwable e) {
//            // 异常处理
//            doAfterThrowing(joinPoint, e);
//            throw e;
//        } finally {
//            // 清理ThreadLocal
//            startTime.remove();
//            requestId.remove();
//        }
//    }

    /**
     * 前置通知：在目标方法执行之前执行
     */
    @Before("operLogPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                // 生成请求ID
                requestId.set(UUID.randomUUID().toString().replace("-", ""));
                HttpServletRequest request = attributes.getRequest();
                startTime.set(System.currentTimeMillis());

                // 获取请求信息
                String ip = getRemoteHost(request);
                String httpMethod = request.getMethod();
                String targetClass = joinPoint.getTarget().getClass().getName();
                // targetClass截取最后一个点
                targetClass = targetClass.substring(targetClass.lastIndexOf(".") + 1);

                // 根据请求方法类型获取对应参数
                String requestParam = StringUtils.EMPTY;
                if (Method.GET.name().equalsIgnoreCase(httpMethod)) {
                    requestParam = truncateString(getUrlParam(request), MAX_PARAM_LENGTH);
                } else if (Method.POST.name().equalsIgnoreCase(httpMethod)) {
                    if (ObjectUtils.isNotEmpty(joinPoint.getArgs())) {
                        requestParam = truncateString(JSONObject.toJSONString(joinPoint.getArgs()[0]), MAX_PARAM_LENGTH);
                    }
                }

                // 记录请求日志
                log.info("[{}] 请求日志 - IP:[{}], 方法:[{}], 类:[{}], URI:[{}], 参数:[{}]",
                        requestId.get(), ip, httpMethod, targetClass, request.getRequestURI(), requestParam);
            }
        } catch (Exception e) {
            log.error("记录请求日志异常", e);
        }
    }

    /**
     * 后置通知：在目标方法执行之后执行
     */
    @AfterReturning(pointcut = "operLogPointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        try {
            long executionTime = System.currentTimeMillis() - startTime.get();
            String resultStr = result != null ? truncateString(JSONObject.toJSONString(result), MAX_RESPONSE_LENGTH) : "null";
            log.info("[{}] 响应日志 - 方法:[{}], 耗时:[{}ms], 返回结果:[{}]",
                    requestId.get(),
                    joinPoint.getSignature().getName(),
                    executionTime,
                    resultStr);
        } catch (Exception e) {
            log.error("记录响应日志异常", e);
        }
    }

    /**
     * 异常通知：在目标方法抛出异常时执行
     */
    private void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try {
            long executionTime = System.currentTimeMillis() - startTime.get();
            log.error("[{}] 异常日志 - 方法:[{}], 耗时:[{}ms], 异常信息:[{}]",
                    requestId.get(),
                    joinPoint.getSignature().getName(),
                    executionTime,
                    e.getMessage());
        } catch (Exception ex) {
            log.error("记录异常日志异常", ex);
        }
    }

    /**
     * 获取URL参数
     */
    private String getUrlParam(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        StringJoiner urlParam = new StringJoiner(", ");
        parameterMap.forEach((key, values) -> {
            for (String value : values) {
                urlParam.add(key + "=" + value);
            }
        });
        return urlParam.toString();
    }

    /**
     * 获取请求IP地址
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        boolean isUnknown = StringUtils.isNotBlank(ip) || "unknown".equalsIgnoreCase(ip);
        if (isUnknown) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (isUnknown) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (isUnknown) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    /**
     * 字符串截断处理
     *
     * @param str       原始字符串
     * @param maxLength 最大长度
     * @return 截断后的字符串
     */
    private String truncateString(String str, int maxLength) {
        if (str == null) {
            return "null";
        }
        if (str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...(总长度:" + str.length() + ")";
    }
}